// init: The initial user-level program

#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"

char *argv[] = { "sh", 0 };

// 此程序开始，第一个进程initproc生成了一个子进程并exec启动shell，然后自己本身就开始了不停轮询回收僵尸进程的工作（最底下的while）

int
main(void)
{
  int pid, wpid;

  //在需要的情况下创建一个新的控制台设备文件，然后把它作为描述符0，1，2打开
  if(open("console", O_RDWR) < 0){// console文件不存在时返回-1
    mknod("console", 1, 1);  //如果"console"文件不存在，这行代码会使用mknod系统调用来创建它，第一个参数是文件名，主设备号1，辅设备号1，这两个设备号唯一确定一个内核设备
                             //当一个进程之后打开这个文件的时候，内核将读、写的系统调用转发到内核设备的实现上，而不是传递给文件系统
    open("console", O_RDWR);   // stdin  文件描述符0
  }
  
  // dup(fd)复制一个已有的文件描述符fd，返回一个指向同一个输入/输出对象的新描述符。这两个描述符共享一个文件偏移
  // 这种操作通常用于重定向标准输入、输出和错误流到特定的设备或文件。
  dup(0);  // stdout  文件描述符1，此时还不是stdout
  dup(0);  // stderr  文件描述符2，此时还不是stderr
  //在sh程序中对输入输出进行重定向

  for(;;){  // 不断轮询
    printf(1, "init: starting sh\n");
    pid = fork();
    if(pid < 0){//进程创建失败
      printf(1, "init: fork failed\n");
      exit();
    }
    if(pid == 0){   //子进程 加载shell程序
      exec("sh", argv);  //如果运行的话，exec是不会返回的，下面的两句是不会执行的
      printf(1, "init: exec sh failed\n");//执行到这的话就是出错了
      exit();
    }
    while((wpid=wait()) >= 0 && wpid != pid)  // 不断处理没有父进程的僵尸进程，直到 shell 退出，然后for循环再反复，当wpid=pid时，就意味着shell退出了
      printf(1, "zombie!\n");//如果进入，则说明wpid不等于pid即shell没退出，然后wait()回收了一个子进程，但不是shell 不是自己的子进程，所以是僵尸zombie进程
  }
}
